package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.api.IWchannelClientFeign;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.message.vos.ArticleVisitStreamMess;
import com.heima.model.wemedia.pojos.WmChannel;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IWchannelClientFeign wchannelClientFeign;
    /**
     * 定时任务执行计算热点文章
     */
    @Override
    //加上一个定时任务
    @XxlJob(value = "HotArticleJobHandle")
    public void computeHotArticle() {
        //1.查询当前日期之前的前五天的文章列表
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.DATE,-5);
        Date before5Day=calendar.getTime();
        List<ApArticle> articleListByLast5days = apArticleMapper.findArticleListByLast5days(before5Day);
        //2.对这些文章进行分值计算
         List<HotArticleVo> articleScoreList=jisuanArticleScore(articleListByLast5days);

        //3.根据频道缓存数据到redis中(最多30条数据)
            //3.1 获取所有频道列表
        ResponseResult<List<WmChannel>>responseResult = wchannelClientFeign.findAll();
        if(responseResult.getCode()!=200){
            return;
        }
        List<WmChannel> channelList =  responseResult.getData();
        //3.2 根据频道检索对应的文章，分值倒叙排序，取出30条数据
        if(channelList==null || channelList.size()==0){
            return;
        }
        for (WmChannel wmChannel : channelList) {
            //获取了每个频道下的对应的文章集合
            List<HotArticleVo> channelHotArticleVoList = articleScoreList.stream().filter(hotArticleVo -> wmChannel.getId().equals(hotArticleVo.getChannelId())).collect(Collectors.toList());
            //根据分值降序排序
            cacheHotArticleToRedis(channelHotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + wmChannel.getId());
        }

        //4.根据推荐缓存数据到redis中(最多30条数据)
            //4.1 根据所有的文章，分值倒叙排序，取出30条数据
        cacheHotArticleToRedis(articleScoreList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.TUIJIAN_KEY_SUFFIX);
    }



    /**
     * 抽取的第三个方法，缓存数据到redis
     * @param list
     * @param key
     */
    private void cacheHotArticleToRedis(List<HotArticleVo> list, String key) {
        list = list.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
        //取出前三十条分值较高的文章
        list = list.stream().limit(30).collect(Collectors.toList());
        //3.3 缓存到redis中缓存数据到redis中
        redisTemplate.opsForValue().set(key, JSON.toJSONString(list));
    }

    /**
     * 抽取的方法一，计算文章分值
     * @param articleListByLast5days 前五天的文章列表集合
     * @return
     */
    private List<HotArticleVo> jisuanArticleScore(List<ApArticle> articleListByLast5days) {
        //1.判断当前list是否为空
        if(articleListByLast5days!=null && articleListByLast5days.size()>0){
            //2.遍历集合
            List<HotArticleVo> hotArticleVoList = articleListByLast5days.stream().map(apArticle -> {
                //实例化HotArticleVo
                HotArticleVo vo = new HotArticleVo();
                //对象参数拷贝
                BeanUtils.copyProperties(apArticle,vo);
                //计算分值
                Integer score=computeArticleScore(apArticle);
                vo.setScore(score);
                return vo;
            }).collect(Collectors.toList());

            return hotArticleVoList;
        }
        return null;
    }

    /**
     * 抽取的第二个方法，计算每一篇文章的分值
     * @param apArticle
     * @return
     */
    private Integer computeArticleScore(ApArticle apArticle) {
       //定义一个初始化分值
       Integer score=0;
       if(apArticle.getLikes()!=null){
           score+=apArticle.getLikes()* ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
       }
       if(apArticle.getCollection()!=null){
           score+=apArticle.getCollection()*ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
       }
       if(apArticle.getComment()!=null){
           score+=apArticle.getComment()*ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
       }
       if(apArticle.getViews()!=null){
           score+=apArticle.getViews();
       }
       return score;
    }


    /**
     * 更新文章的分值  同时更新缓存中的热点文章数据
     *
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        //1.修改ap_article表中的行为数量
        ApArticle apArticle = apArticleMapper.selectById(mess.getArticleId());
        if(apArticle==null){
            return;
        }
        apArticle.setLikes(apArticle.getLikes()+mess.getLike());
        apArticle.setViews(apArticle.getViews()+mess.getView());
        apArticle.setCollection(apArticle.getCollection()+mess.getCollect());
        apArticle.setComment(apArticle.getComment()+mess.getComment());
        apArticleMapper.updateById(apArticle);
        //2.重新计算分值
        Integer score = this.computeArticleScore(apArticle);
        score=score*3;

        //3.替换redis中的数据
            //3.1 按照频道来替换
        cacheHotArticleToRedis(mess, apArticle, score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());

        //3.2 按照推荐来替换
        cacheHotArticleToRedis(mess, apArticle, score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+"__all__");
    }

    /**
     * 缓存数据到redis中，重新计算分值并替换数据
     * @param mess
     * @param apArticle
     * @param score
     * @param key
     */
    private void cacheHotArticleToRedis(ArticleVisitStreamMess mess, ApArticle apArticle, Integer score,String key) {

        //根据频道的key去查询结果
        String hotArticleVoListStr = redisTemplate.opsForValue().get(key);
        //转化成list集合
        List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleVoListStr, HotArticleVo.class);

        Boolean flag=true;

        if(hotArticleVoList!=null && hotArticleVoList.size()>0){
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                //表示该文章在缓存中存在
                if(hotArticleVo.getId().equals(mess.getArticleId())){
                    //更新了改文章的分值
                    hotArticleVo.setScore(score);
                    flag=false;
                    break;
                }
            }
            //表示在缓存中不存在
            if(flag){
                //判断缓存中的长度
                if(hotArticleVoList.size()<30){
                    //表示缓存中的长度在30条以内，直接添加到redis中
                    HotArticleVo hot = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hot);
                    hot.setScore(score);
                    hotArticleVoList.add(hot);
                }else{
                    //表示缓存中的长度超过30条
                        //倒叙排序
                    hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
                        //取出最后一个元素
                    HotArticleVo lastHotArticleVo = hotArticleVoList.get(hotArticleVoList.size() - 1);
                        //比较分值,如果最后一个元素的分值大于最新的分值，则不管，反之，则替换元素
                    if(lastHotArticleVo.getScore()< score){
                        //从list中删除掉最后一个元素
                        hotArticleVoList.remove(lastHotArticleVo);
                        HotArticleVo hot = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hot);
                        hot.setScore(score);
                        //在list中添加新元素
                        hotArticleVoList.add(hot);
                    }
                }
            }
            //再次进行倒序展示
            hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
            redisTemplate.opsForValue().set(key,JSON.toJSONString(hotArticleVoList));
        }
    }


}
